package com.mm.base.config.jwt;

import cn.hutool.json.JSONUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.mm.base.annotation.VerifyToken;
import com.mm.base.constant.BasicConstants;
import com.mm.base.exception.MuMuException;
import com.mm.util.result.DResult;
import com.mm.domain.user.DUser;
import com.mm.serivce.common.TokenService;
import com.mm.serivce.user.IDUserService;
import com.mm.util.common.RedisUtil;
import com.mm.util.common.RedisKey;
import com.mm.vo.mp.MiniUserVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;

/**
 * 描述: 拦截器去获取token并验证token
 *
 * @author: yanglin
 * @Date: 2021-08-04-17:04
 * @Version: 1.0
 */
@Slf4j
public class AuthenticationInterceptor implements HandlerInterceptor {

    @Autowired
    private IDUserService userService;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private RedisUtil redisUtil;


    /**
     * 处理回调方法,实现处理器的预处理，第三个参数为响应的处理器,自定义Controller,返回值为true表示继续流程（如调用下一个拦截器或处理器）或者接着执行
     * postHandle()和afterCompletion()；false表示流程中断，不会继续调用其他的拦截器或处理器，中断执行。
     *
     * @param request
     * @param response
     * @param object
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
        // 如果不是映射到方法直接通过
        if (!(object instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) object;
        Method method = handlerMethod.getMethod();

        if (method.isAnnotationPresent(VerifyToken.class)) {
            VerifyToken verifyToken = method.getAnnotation(VerifyToken.class);
            // 判断是否验证
            if (verifyToken.required()) {
                // 判断验证Token类型
                int type = verifyToken.type();
                // 0-WEB
                if (type == 0) {
                    // 从 http 请求头中取出 token WEB
                    String token = request.getHeader(BasicConstants.WEB_TOKEN);
                    // 执行认证
                    if (token == null) {
                        throw new MuMuException("无token，请重新登录");
                    }
                    // 缓存中读取用户数据
                    String userStr = (String) redisUtil.get(RedisKey.WEB_TOKEN.getValue() + token);
                    DUser user = JSONUtil.toBean(userStr, DUser.class);
                    if (user == null) {
                        throw new MuMuException("用户不存在，请重新登录");
                    }
                    // 验证 token
                    JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getUserPassword())).build();
                    try {
                        jwtVerifier.verify(token);
                    } catch (JWTVerificationException e) {
                        throw new MuMuException("401");
                    }
                    return true;
                } else if (type == 1) {
                    // 1-MP
                    // 从 http 请求头中取出 token 小程序 (openId)
                    String mpToken = request.getHeader(BasicConstants.MP_TOKEN);
                    if (mpToken == null) {
                        throw new MuMuException("无token，请重试");
                    }
                    // 缓存中读取用户数据
                    String userStr = (String) redisUtil.get(RedisKey.MP_TOKEN.getValue() + mpToken);
                    MiniUserVo user = JSONUtil.toBean(userStr, MiniUserVo.class);
                    if (user == null) {
                        throw new MuMuException("用户不存在，请重新登录");
                    }
                }
            }

        }
        return true;
    }

    /**
     * 后处理回调方法，实现处理器的后处理（DispatcherServlet进行视图返回渲染之前进行调用），
     * 此时我们可以通过modelAndView（模型和视图对象）对模型数据进行处理或对视图进行处理，modelAndView也可能为null。
     *
     * @param httpServletRequest
     * @param httpServletResponse
     * @param o
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest httpServletRequest,
                           HttpServletResponse httpServletResponse,
                           Object o, ModelAndView modelAndView) throws Exception {

    }

    /**
     * 整个请求处理完毕回调方法,该方法也是需要当前对应的Interceptor的preHandle()的返回值
     * 为true时才会执行，也就是在DispatcherServlet渲染了对应的视图之后执行。用于进行资源清理。
     * 整个请求处理完毕回调方法。如性能监控中我们可以在此记录结束时间并输出消耗时间，还可以进行一些资源清理，类似于try-catch-finally中的finally，但仅调用处理器执行链中
     *
     * @param request
     * @param response
     * @param o
     * @param e
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object o, Exception e) throws Exception {
    }

    /**
     * 返回的json值
     *
     * @param response
     * @param result
     * @throws Exception
     */
    private void writeReturnJson(HttpServletResponse response, DResult result) {
        PrintWriter writer = null;
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html; charset=utf-8");
        try {
            writer = response.getWriter();
            writer.print(JSONUtil.toJsonStr(result));

        } catch (IOException e) {
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }
}
